package com.ruoyi.project.business.dailyschedule.service.impl;

import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.project.business.dailyschedule.entity.DailySchedule;
import com.ruoyi.project.business.dailyschedule.mapper.DailyScheduleMapper;
import com.ruoyi.project.business.dailyschedule.request.AIAssignDailyScheduleRequest;
import com.ruoyi.project.business.dailyschedule.request.AddDailyScheduleRequest;
import com.ruoyi.project.business.dailyschedule.request.ListDailyScheduleRequest;
import com.ruoyi.project.business.dailyschedule.request.RequiredPost;
import com.ruoyi.project.business.dailyschedule.response.DailyScheduleResponse;
import com.ruoyi.project.business.dailyschedule.service.IDailyScheduleService;
import com.ruoyi.project.business.dailyschedulerule.service.IDailyscheduleRuleService;
import com.ruoyi.project.business.domain.entity.DailyScheduleEmp;
import com.ruoyi.project.business.domain.model.daily.DailyScheduleSolution;
import com.ruoyi.project.business.domain.model.daily.Post;
import com.ruoyi.project.business.domain.model.daily.TimePeriodMirror;
import com.ruoyi.project.business.domain.rules.Rule;
import com.ruoyi.project.business.domain.rules.daily.EmpAssignTimeNotRepeat;
import com.ruoyi.project.business.domain.rules.daily.EmpSkillsAssign;
import com.ruoyi.project.business.domain.strategy.CommonDailyStrategy;
import com.ruoyi.project.business.domain.utils.RuleUtil;
import com.ruoyi.project.business.tmpuserpost.service.ITmpUserpostService;
import com.ruoyi.project.business.utils.BusinessUtil;
import com.ruoyi.project.system.entity.SysUser;
import com.ruoyi.project.system.service.ISysUserService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 日排班Service业务层处理
 * 
 * @author ${author}
 * @date 2024-03-22
 */
@Service
public class DailyScheduleServiceImpl implements IDailyScheduleService {
    @Autowired
    private DailyScheduleMapper dailyScheduleMapper;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IDailyscheduleRuleService dailyscheduleRuleService;
    @Autowired
    private ITmpUserpostService userpostService;

    /**
     * 查询日排班
     * 
     * @return 日排班
     */
    @Override
    public DailySchedule selectDailyScheduleById(Long id) {
        return dailyScheduleMapper.selectDailyScheduleById(id);
    }

    /**
     * 查询日排班列表
     * 
     * @return 日排班
     */
    @Override
    public List<DailyScheduleResponse> selectDailyScheduleList(ListDailyScheduleRequest request, Long tenantId) {
        DailySchedule param = BeanUtils.copy(request, DailySchedule.class);
        LocalDate date = request.getDate();
        if(date == null) {
            date = LocalDate.now();
        }
        param.setYear(date.getYear());
        param.setMonth(date.getMonthValue());
        param.setDayOfMonth(date.getDayOfMonth());
        param.setTenantId(tenantId);
        List<DailyScheduleResponse> resList = dailyScheduleMapper.selectDailyScheduleList(param);

//        BusinessUtil.fillDeptNameUserNameCityName(resList, tenantId);
        return resList;
    }

    /**
     * 新增日排班
     * 
     * @return 结果
     */
    @Override
    public int insertDailySchedule(AddDailyScheduleRequest request, Long operatorId, Long tenantId) {
        DailySchedule entity = BeanUtils.copy(request, DailySchedule.class);
        entity.setTenantId(tenantId);
        entity.setCreator(operatorId);
        entity.setDeleted(Boolean.FALSE);
        entity.setCreateTime(DateUtils.getNowDate());
        return dailyScheduleMapper.insertDailySchedule(entity);
    }

    /**
     * 修改日排班
     * 
     * @return 结果
     */
    @Override
    public int updateDailySchedule(DailySchedule entity, Long operatorId, Long tenantId) {
        entity.setUpdateTime(DateUtils.getNowDate());
        entity.setTenantId(tenantId);
        entity.setUpdater(operatorId);
        return dailyScheduleMapper.updateDailySchedule(entity);
    }

    /**
     * 批量删除日排班
     * 
     * @return 结果
     */
    @Override
    public int deleteDailyScheduleByIds(Long[] ids, Long operatorId, Long tenantId) {
        return dailyScheduleMapper.deleteDailyScheduleByIds(ids);
    }

    /**
     * 删除日排班信息
     * 
     * @return 结果
     */
    @Override
    public int deleteDailyScheduleById(Long id, Long operatorId, Long tenantId) {
        return dailyScheduleMapper.deleteDailyScheduleById(id);
    }

    @Override
    public int aiAssign(AIAssignDailyScheduleRequest request, Long operatorId, Long tenantId) {
        LocalDate assignDate = request.getAssignDate();
        List<RequiredPost> requiredPosts = request.getRequiredPosts();
        List<TimePeriodMirror> requirments = genRequirments(requiredPosts, assignDate);

        List<DailyScheduleEmp> emps = getDailyScheduleEmps(requiredPosts, assignDate, tenantId);

        // TODO 可以考虑做成异步的
        CommonDailyStrategy commonStrategy = new CommonDailyStrategy();
        DailyScheduleSolution solution = commonStrategy.doDailyAssign(emps, requirments);
        Map<Long, List<TimePeriodMirror>> userShifts = solution.getUserShifts();
        save(userShifts, assignDate, operatorId, tenantId);
        return 0;
    }

    private void save(Map<Long, List<TimePeriodMirror>> userShifts, LocalDate assignDate, Long operatorId, Long tenantId) {
        Date nowDate = DateUtils.getNowDate();
        int month = assignDate.getMonthValue();
        int dayOfMonth = assignDate.getDayOfMonth();
        int year = assignDate.getYear();
        int dayOfWeek = assignDate.getDayOfWeek().getValue();

        List<Long> userIds = new ArrayList<>(userShifts.keySet());
        this.deleteDailyScheduleByUserIdAndDate(userIds, year, month, dayOfMonth, tenantId);

        List<DailySchedule> resList = new ArrayList<>();
        userShifts.forEach((userId, timePeriodMirrors) -> {
            if(CollectionUtils.isNotEmpty(timePeriodMirrors)) {

                DailySchedule entity = new DailySchedule();
                entity.setUserId(userId);
                entity.setTenantId(tenantId);
                entity.setCreator(operatorId);
                entity.setDeleted(Boolean.FALSE);
                entity.setCreateTime(nowDate);
                entity.setYear(year);
                entity.setMonth(month);
                entity.setDayOfMonth(dayOfMonth);
                entity.setDayOfWeek(dayOfWeek);

                timePeriodMirrors.forEach(timePeriodMirror -> {
                    int index = dailyIndex(timePeriodMirror.getSegmentStart(), timePeriodMirror.getSegmentEnd());
//                long value = timePeriodMirror.getTimePeriodId();
                    long value = timePeriodMirror.getTmpPostId();
                    String fieldName = "scheime" + index;
                    // 获取 MonthSchedule 类的 dayOfMonth 字段
                    BusinessUtil.setFieldName(entity, fieldName, value);
                });

                resList.add(entity);
            }
        });
        this.batchInsert(resList);
    }

    private void deleteDailyScheduleByUserIdAndDate(List<Long> userIds, int year, int month, int dayOfMonth, Long tenantId) {
        if(CollectionUtils.isNotEmpty(userIds)) {
            this.dailyScheduleMapper.deleteDailyScheduleByUserIdAndDate(userIds, year, month, dayOfMonth, tenantId);
        }
    }

    private int dailyIndex(LocalTime segmentStart, LocalTime segmentEnd) {
        int hour = segmentStart.getHour();
        int minute = segmentStart.getMinute();
        return hour * 2 + (minute >= 30 ? 1 : 0);
    }

    private void batchInsert(List<DailySchedule> resList) {
        if(CollectionUtils.isNotEmpty(resList)) {
            this.dailyScheduleMapper.batchInsert(resList);
        }
    }

    private List<DailyScheduleEmp> getDailyScheduleEmps(List<RequiredPost> requiredPosts, LocalDate assignDate, Long tenantId) {
        List<Long> postIds = requiredPosts.stream().map(tmp -> tmp.getPostId()).collect(Collectors.toList());
        List<SysUser> users = sysUserService.selectUserListByPostIds(postIds, tenantId);

        if(CollectionUtils.isNotEmpty(users)) {
            List<Long> userIds = users.stream().map(SysUser::getUserId).distinct().collect(Collectors.toList());
            Map<Long, List<Rule>> rulesMap = dailyscheduleRuleService.selectRulesMapByUserIds(userIds, null, tenantId);

            Map<Long, List<Post>> tmpUserpostsMap = userpostService.selectTmpUserpostByUserIds(userIds, tenantId);

            // 还有要个全局规则，用于控制管理者的需求。就完美了
            List<Rule> globalRules = getDailySolutionRules(); // TODO

            List<DailyScheduleEmp> emps = users.stream().map(user -> {
                DailyScheduleEmp emp = new DailyScheduleEmp();
                emp.setUserId(user.getUserId());
                if(tmpUserpostsMap.containsKey(user.getUserId())) {
                    emp.setEmpPosts(tmpUserpostsMap.get(user.getUserId()));
                }else {
                    throw new CustomException("请为员工 " + user.getNickName() + " 添加岗位标签", -1);
                }

                List<Rule> rules = rulesMap.getOrDefault(user.getUserId(), new ArrayList<>());
                HashSet<Rule> tmpSet = new HashSet<>(rules);
                tmpSet.addAll(globalRules);
                emp.setRules(new ArrayList<>(tmpSet));

                return emp;
            }).collect(Collectors.toList());

            return emps;
        }
        throw new CustomException("未知的用户", -1);
    }

    private List<TimePeriodMirror> genRequirments(List<RequiredPost> requiredPosts, LocalDate assignDate) {
        List<TimePeriodMirror> collect = requiredPosts.stream().map(tmp -> {
            List<TimePeriodMirror> tmpList = new ArrayList<>();
            // 把 tmp.getAssignTimeStart() ~ tmp.getAssignTimeEnd() 拆解成 30 分钟一个单位的
            List<TimePeriodMirror> periodOf30Minutes = DateUtils.splitTo30Minute(tmp.getAssignTimeStart(), tmp.getAssignTimeEnd());
            for (Integer i = 0; i < tmp.getRequireNum(); i++) {
                for (TimePeriodMirror periodOf30Minute : periodOf30Minutes) {
                    TimePeriodMirror timePeriodMirror = new TimePeriodMirror();
                    timePeriodMirror.setSegmentStart(periodOf30Minute.getSegmentStart());
                    timePeriodMirror.setSegmentEnd(periodOf30Minute.getSegmentEnd());
                    timePeriodMirror.setTimePeriodId(tmp.getId());
                    timePeriodMirror.setTmpPostId(tmp.getPostId());
                    timePeriodMirror.setTmpPostName("");
                    tmpList.add(timePeriodMirror);
                }
            }
            return tmpList;
        })
        .flatMap(List::stream)
        .collect(Collectors.toList());
        return collect;
    }

    private List<Rule> getDailySolutionRules() {
        EmpAssignTimeNotRepeat timeNotRepeat = (EmpAssignTimeNotRepeat)RuleUtil.initializeDailyRuleBean(EmpAssignTimeNotRepeat.class.getSimpleName(), 1, "同一个人时段不重复", false);
        List<Rule> globalRules = new ArrayList<>();
        globalRules.add(timeNotRepeat);


        EmpSkillsAssign skillsAssign = (EmpSkillsAssign)RuleUtil.initializeDailyRuleBean(EmpSkillsAssign.class.getSimpleName(), 1, "同一个人时段不重复", false);
        globalRules.add(skillsAssign);
        return globalRules;
    }
}
